﻿using System.Linq.Expressions;
using System.Reflection;
using ApiLib.AppStart;
using EnumsNET;
using Furion;

namespace ApiLib.Utils;

/// <summary>
/// 反射
/// </summary>
public static class AssemblyUtil
{
    private static readonly string[] IgnoreAssemblyNames =
    {
        "Microsoft.", "System.", "netstandard", "mscorlib", "Enums.Net", "Swashbuckle.", "Newtonsoft", "MathNet",
        "ICSharpCode", "Kdbndp", "ZstdSharp", "ExtendedNumerics", "DnsClient", "Dm.", "BouncyCastle", "AWSSDK",
        "Furion.", "MiniProfiler", "Ben.Demystifier", "BCrypt.Net",
        "SqlSugar", "MySqlConnector", "SQLite", "MongoDB", "Oscar", "Oracle", "Npgsql",
        "NPOI", "SixLabors", "SkiaSharp", "SharpCompress",
        "WebDav", "CSRedisCore", "Caching.CsRedis",
    };

    private static readonly List<Assembly> LoadAssemblies = new();

    /// <summary>
    /// 获取可以扫描的assembly
    /// </summary>
    /// <returns></returns>
    public static List<Assembly> GetLoadAssemblies()
    {
        if (LoadAssemblies.Count == 0)
        {
            var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in allAssemblies)
            {
                var name = assembly.GetName().Name;
                if (name == null)
                {
                    continue;
                }

                //判断是否在忽略列表
                var first = IgnoreAssemblyNames.FirstOrDefault(s => name.StartsWith(s));
                if (!string.IsNullOrEmpty(first))
                {
                    continue;
                }

                LoadAssemblies.Add(assembly);
            }
        }

        return LoadAssemblies;
    }

    /// <summary>
    /// 获取所有类型（非接口，非抽象类）
    /// </summary>
    /// <param name="where">条件</param>
    /// <returns></returns>
    public static List<Type> GetTypes(Func<Type, bool>? where = null)
    {
        var assemblies = GetLoadAssemblies();
        var types = new List<Type>();
        foreach (var assembly in assemblies)
        {
            try
            {
                var enumerable = assembly.GetTypes() //获取当前类库下所有类型
                    .Where(t => where?.Invoke(t) ?? true);
                types.AddRange(enumerable);
            }
            catch (Exception)
            {
                var msg = $"无法加载{assembly.GetName()}";
                Console.WriteLine(msg);
                msg.LogError();
            }
        }

        return types;
    }

    /// <summary>
    /// 扫描所有类
    /// </summary>
    /// <param name="action"></param>
    public static void ScanAllTypes(Action<Type> action)
    {
        var assemblies = AssemblyUtil.GetLoadAssemblies();
        foreach (var assembly in assemblies)
        {
            var types = assembly.GetTypes();
            foreach (var type in types)
            {
                action.Invoke(type);
            }
        }
    }

    /// <summary>
    /// 获取间接或直接继承
    /// </summary>
    /// <param name="t"></param>
    /// <param name="types"></param>
    /// <returns></returns>
    private static bool IsAssignableTo(this Type t, params Type[] types)
    {
        foreach (var type in types)
        {
            if (t.IsAssignableTo(type))
            {
                return true;
            }
        }

        return false;
    }

    /// <summary>
    /// 获取继承的子类（非接口，非抽象类）
    /// </summary>
    /// <param name="types"></param>
    /// <returns></returns>
    public static List<Type> GetChildTypes(params Type[] types)
    {
        var allTypes = new List<Type>();
        foreach (var type in types)
        {
            allTypes.AddRange(type.GetChildTypes());
        }

        return allTypes;
        
        /*var assemblyNames = GetAssemblyNames();
        var list = new List<Type>();
        foreach (var assemblyName in assemblyNames)
        {
            try
            {
                // var assembly = Assembly.LoadFrom(assemblyName.Name);
                var assembly = Assembly.Load(assemblyName);
                var enumerable = assembly.GetTypes() //获取当前类库下所有类型
                    .Where(t => t.IsAssignableTo(types)) //获取间接或直接继承t的所有类型
                    .Where(t => t is { IsAbstract: false, IsClass: true }); //获取非抽象类 排除接口继承
                list.AddRange(enumerable);
            }
            catch (Exception)
            {
                var msg = assemblyName + "找不到文件，无法加载Assembly";
                Console.WriteLine(msg);
                msg.LogError();
            }
        }

        return list;*/
    }

    /// <summary>
    /// 获取继承的子类（非接口，非抽象类）
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    public static List<Type> GetChildTypes(this Type type)
    {
        return GetTypes(t => t.IsAssignableTo(type) //获取间接或直接继承t的所有类型
                             && t is { IsAbstract: false, IsClass: true } //获取非抽象类 排除接口继承
        );
    }

    /// <summary>
    /// 创建实例
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static T? GetInstance<T>(this Type type)
    {
        var instance = (T)Activator.CreateInstance(type)!;
        return instance; //创造实例，并返回结果（项目需求，可删除）
    }
    
    /// <summary>
    /// 创建实例
    /// </summary>
    /// <returns></returns>
    public static object? GetInstance(this Type type)
    {
        var instance = Activator.CreateInstance(type)!;
        return instance; //创造实例，并返回结果（项目需求，可删除）
    }

    /// <summary>
    /// 获取继承的子类（非接口，非抽象类）的实例化对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static IEnumerable<T?> GetChildInstances<T>()
    {
        return GetChildTypes(typeof(T))
            .Select(t => t.GetInstance<T>()); //创造实例，并返回结果（项目需求，可删除）
    }

    /// <summary>
    /// 获取方法
    /// </summary>
    /// <param name="uri">程序集:命名空间.类.方法 => Test.OA:Test.Oa.Class1.Test1()</param>
    /// <returns></returns>
    public static MethodInfo? LoadMethodInfo(string uri)
    {
        try
        {
            var strings = uri.Split(":");
            var assemblyName = strings[0];
            var lastPointIndex = strings[1].LastIndexOf(".", StringComparison.Ordinal);
            var classPath = strings[1][..lastPointIndex];
            var method = strings[1][(lastPointIndex + 1)..].Replace("()", "");

            var assembly = Assembly.Load(assemblyName);
            var type = assembly.GetType(classPath);
            if (type != null)
            {
                var methodInfo = type.GetMethod(method);
                return methodInfo;
            }
        }
        catch (Exception e)
        {
            e.Message.LogError();
        }

        return default;
    }

    /// <summary>
    /// 执行方法
    /// </summary>
    /// <param name="uri">程序集:命名空间.类.方法 => Test.OA:Test.Oa.Class1.Test1()</param>
    /// <param name="args">参数</param>
    public static void RunMethod(string uri, object?[]? args = null)
    {
        var load = LoadMethodInfo(uri);
        if (load == null) return;
        if (load.IsStatic)
        {
            load.Invoke(null, args);
        }
        else if (load.DeclaringType != null)
        {
            var obj = Activator.CreateInstance(load.DeclaringType);
            load.Invoke(obj, args);
        }
    }

    /// <summary>
    /// 运行方法
    /// </summary>
    /// <param name="methodInfo"></param>
    /// <param name="args"></param>
    public static void RunMethod(MethodInfo methodInfo, object?[]? args = null)
    {
        if (methodInfo.IsStatic)
        {
            methodInfo.Invoke(null,args);
        }
        else
        {
            var type = methodInfo.DeclaringType;
            var instance = App.GetService(type) ?? type?.GetInstance();
            if (instance!=null)
            {
                methodInfo.Invoke(instance, args);
            }
        }
    }

    /// <summary>
    /// 获取属性名称
    /// </summary>
    /// <param name="expr"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    [Obsolete]
    public static string GetPropName<T>(Expression<Func<T, object>> expr)
    {
        return expr.Body switch
        {
            UnaryExpression unaryExpression => ((MemberExpression)unaryExpression.Operand).Member.Name,
            MemberExpression memberExpression => memberExpression.Member.Name,
            ParameterExpression parameterExpression => parameterExpression.Type.Name,
            _ => ""
        };
    }
}